package org.chintoooor.trio.server.accept.task;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;

import org.chintoooor.trio.server.Server;
import org.chintoooor.trio.server.http.task.Http;
import org.chintoooor.trio.server.http.task.HttpTaskModel;

public class Accept implements Runnable {

	private final Logger logger = Logger.getLogger(Http.class
			.getCanonicalName());

	private final AtomicBoolean isUp;
	private final AcceptorTaskModel acceptorThreadModel;

	public Accept(AtomicBoolean isUp, AcceptorTaskModel acceptorThreadModel) {
		super();
		this.isUp = isUp;
		this.acceptorThreadModel = acceptorThreadModel;
	}

	@Override
	public void run() {
		logger.entering(Accept.class.getCanonicalName(), "run");
		try {
			while (isUp.get()) {
				Selector acceptSelector = acceptorThreadModel
						.getAcceptSelector();
				acceptSelector.select(Server.TIMEOUT);
				Set<SelectionKey> selectedKeys = acceptSelector.selectedKeys();
				Iterator<SelectionKey> selectedKeysIterator = selectedKeys
						.iterator();
				while (selectedKeysIterator.hasNext()) {
					SelectionKey currentSelectionKey = selectedKeysIterator
							.next();
					if ((currentSelectionKey.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {

						List<HttpTaskModel> listenerThreadModels = acceptorThreadModel
								.getListenerThreadModels();
						HttpTaskModel readWriteSelector = listenerThreadModels
								.get(ThreadLocalRandom.current().nextInt(
										Server.LISTENERS_THREADS));

						ServerSocketChannel serverSocketChannel = (ServerSocketChannel) currentSelectionKey
								.channel();
						SocketChannel localSocketChannel = serverSocketChannel
								.accept();
						localSocketChannel.configureBlocking(false);

						logger.fine("Got connection from " + localSocketChannel);

						readWriteSelector.submit(localSocketChannel);
						readWriteSelector.getSelector().wakeup();

						selectedKeysIterator.remove();
					}
				}
			}
		} catch (IOException ioException) {
			logger.warning("The thread will now exit due to " + ioException);
			throw new RuntimeException(ioException);
		} catch (RuntimeException runtimeException) {
			logger.warning("The thread will now exit due to "
					+ runtimeException);
			throw runtimeException;
		}
		logger.exiting(Accept.class.getCanonicalName(), "run");
	}

}
